package ua.pp.pycb6a.tranggui;

import java.awt.Desktop;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ResourceBundle;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.BorderPane;
import javafx.stage.FileChooser;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.sun.xml.dtdparser.DTDParser;

public class FrameController {

    private static final String UTF_8 = "UTF-8";

    private static final String XML = "XML";

    private static final String DTD = "DTD";

    private static final String EXT_RNC = ".rnc";

    private static final String EXT_RNG = ".rng";

    private static final String EXT_DTD = ".dtd";

    private static final String EXT_XML = ".xml";

    private static final String EXT_XSD = ".xsd";

    private static final String INPUT_FILE = "tmp_tranggui_file_";

    private static final String JAVA_HOME = "java.home";

    private static final String JAR = "-jar";

    private static final String BIN_JAVA = "/bin/java";

    private static final String TRANG = "lib/trang-20091111.jar";

    private static final String URI_AUTHOR = "http://pycb6a.pp.ua/";

    private static final String URI_TRANG = "http://thaiopensource.com/relaxng/trang-manual.html";

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @FXML
    private ResourceBundle resources;

    @FXML
    private BorderPane root;

    @FXML
    private URL location;

    @FXML
    private TextArea area;

    @FXML
    private RadioButton dtd;

    @FXML
    private Button generate;

    @FXML
    private TextField output;

    @FXML
    private Button select;

    @FXML
    private RadioButton xml;

    @FXML
    private ToggleGroup radioButtons;

    @FXML
    private Hyperlink openDir;

    @FXML
    private Label message;

    private File outputFile;

    private File inputFile;

    private ProcessBuilder processBuilder;

    private String ext;

    private Messages messages = new Messages(log);

    @FXML
    void generate(ActionEvent event) {
        openDir.setVisible(false);
        if (0 == area.getText().length()) {
            messages.traceEmptyInputText(message);
        } else if (output.getText().endsWith(EXT_XSD) || output.getText().endsWith(EXT_DTD)
                || output.getText().endsWith(EXT_RNG) || output.getText().endsWith(EXT_RNC)) {

            outputFile = new File(output.getText()).getAbsoluteFile();
            if (!outputFile.getParentFile().exists()) {
                outputFile.getParentFile().mkdirs();
            }

            inputFile = inputFile();
            if (isValidation(inputFile)) {
                processBuilder = new ProcessBuilder(System.getProperty(JAVA_HOME) + BIN_JAVA, JAR, new File(TRANG)
                        .getAbsoluteFile().getAbsolutePath(), inputFile.getAbsolutePath(), outputFile.getAbsolutePath());
                try {
                    log.warn(processBuilder.command().toString());
                    processBuilder.start();
                    messages.traceSuccess(message);
                    openDir.setVisible(true);
                } catch (Exception e) {
                    messages.traceGenerationFaild(message);
                }
            }
        } else {
            messages.traceIncorrectOutputFile(message);
        }
    }

    private boolean isValidation(File file) {
        if (xml.isSelected()) {
            try {
                DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file);
            } catch (SAXException e) {
                messages.traceSaxError(message, XML, e);
                return false;
            } catch (IOException e) {
                messages.traceIoError(message);
                return false;
            } catch (ParserConfigurationException | FactoryConfigurationError e) {
                messages.traceParserError(message);
                return false;
            }
        }
        if (dtd.isSelected()) {

            try {
                new DTDParser().parse(new InputSource(new FileReader(file)));
            } catch (IOException e) {
                messages.traceIoError(message);
                return false;
            } catch (SAXException e) {
                messages.traceSaxError(message, DTD, e);
                return false;

            }
        }
        return true;
    }

    private File inputFile() {
        File file = null;
        if (xml.isSelected()) {
            ext = EXT_XML;
        }
        if (dtd.isSelected()) {
            ext = EXT_DTD;
        }
        try {
            file = File.createTempFile(INPUT_FILE, ext);
            try (Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), UTF_8))) {
                writer.write(area.getText());
            }
        } catch (IOException e) {
            messages.traceIoError(message);
        }
        return file;
    }

    @FXML
    void select(ActionEvent event) {
        FileChooser fileChooser = new FileChooser();
        outputFile = fileChooser.showOpenDialog(root.getScene().getWindow());
        if (null != outputFile) {
            output.setText(outputFile.getAbsolutePath());
            openDir.setVisible(false);
            message.setText("");
        }
    }

    @FXML
    void navToAuthor(ActionEvent event) {
        if (Desktop.isDesktopSupported()) {
            try {
                Desktop.getDesktop().browse(new URI(URI_AUTHOR));
            } catch (IOException e) {
                messages.traceIoError(message);
            } catch (URISyntaxException e) {
                messages.traceWrongUri(message);
            }
        }
    }

    @FXML
    void navToTrang(ActionEvent event) {
        if (Desktop.isDesktopSupported()) {
            try {
                Desktop.getDesktop().browse(new URI(URI_TRANG));
            } catch (IOException e) {
                messages.traceIoError(message);
            } catch (URISyntaxException e) {
                messages.traceWrongUri(message);
            }
        }
    }

    @FXML
    void openDir(ActionEvent event) {
        if (Desktop.isDesktopSupported()) {
            try {
                Desktop.getDesktop().open(outputFile.getParentFile());
            } catch (IOException e) {
                messages.traceIoError(message);
            }
        }
    }

    @FXML
    void initialize() {
        assert area != null : "fx:id=\"area\" was not injected: check your FXML file 'Frame.fxml'.";
        assert dtd != null : "fx:id=\"dtd\" was not injected: check your FXML file 'Frame.fxml'.";
        assert generate != null : "fx:id=\"generate\" was not injected: check your FXML file 'Frame.fxml'.";
        assert output != null : "fx:id=\"output\" was not injected: check your FXML file 'Frame.fxml'.";
        assert root != null : "fx:id=\"root\" was not injected: check your FXML file 'Frame.fxml'.";
        assert select != null : "fx:id=\"select\" was not injected: check your FXML file 'Frame.fxml'.";
        assert xml != null : "fx:id=\"xml\" was not injected: check your FXML file 'Frame.fxml'.";
        assert radioButtons != null : "fx:id=\"xml\" was not injected: check your FXML file 'Frame.fxml'.";
        assert message != null : "fx:id=\"xml\" was not injected: check your FXML file 'Frame.fxml'.";
        assert openDir != null : "fx:id=\"xml\" was not injected: check your FXML file 'Frame.fxml'.";

    }
}
